;TODO: Watchdog
;TODO: Check presence during initialization sequence

.include "tn13adef.inc"
.include "usbmacro.inc"

.equ F_CPU = 12000000

; Common registers
.def GRB = R0
.def TMP = R16
.def TMP2 = R17
.def ARG0 = R18
.def ARG1 = R19
.def ZERO = R31

; Global variables
.def STATE = R20

; USB states
.def UINIT = R25
.def UIN = R26
.def UOUT = R27
.def J = R28
.def K = R29
.def SE0 = R31

; Pin assignment
.equ MASK_DP1 = (1 << PB0)
.equ MASK_DM1 = (1 << PB2)
.equ MASK_DP2 = (1 << PB1)
.equ MASK_DM2 = (1 << PB4)

; USB masks
.equ PORT1 = MASK_DP1 | MASK_DM1
.equ PORT2 = MASK_DP2 | MASK_DM2
.equ FS_J = MASK_DP1 | MASK_DP2
.equ FS_K = MASK_DM1 | MASK_DM2

; States
.equ ST_NONE = 0
.equ ST_INIT = 1
.equ ST_RESET0 = ST_INIT + 100
.equ ST_RESET1 = ST_RESET0 + 11
.equ ST_SET_ADDR0 = ST_RESET1 + 10
.equ ST_SET_ADDR1 = ST_SET_ADDR0 + 1
.equ ST_SET_CONF0 = ST_SET_ADDR1 + 3
.equ ST_SET_CONF1 = ST_SET_CONF0 + 1

; Two cycles delay
.macro nop2
  cbi PORTB, PB5
.endm

; Three cycles delay
.macro nop3
  lpm
.endm

; Eight cycles delay
.macro nop8
  rcall pnop8
.endm

; Wait for the next "ms" timer ticks, which occur at 1ms intervals
; Do not use when interrupts enabled
.macro wait_ms
  ldi ARG0, @0
  rcall pwait_ms
.endm

.macro case
  cpi STATE, @0
  brne @1
.endm

.macro casel
  cpi STATE, @0
  breq endm
  rjmp @1
endm:
.endm

.macro outi
  ldi TMP, @1
  out @0, TMP
.endm

; Disable port @0
.macro disable
  com @0
  and UOUT, @0
  and J, @0
  and K, @0
.endm

.cseg

.org 0 ; RESET
; Stack setup
  outi SPL, low(RAMEND)
; Constants setup
  clr ZL
  clr ZH
  rjmp start

; ~1kHz timer IRQ handler
.org OC0Aaddr
  usbd UOUT
; send SOF 1337
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs6 J, J, K, J, J, K
  nop2
  usbs2 J, K
  nop3
  usbs4 J, K, K, J
  nop2
  nop3
  usbs4 K, SE0, SE0, J
  usbd UIN
  usbs SE0
; ST_RESET0
  case ST_RESET0, reset0end
  or UIN, UINIT
  or UOUT, UINIT
reset0end:
; ST_RESET1
  case ST_RESET1, reset1end
  clr UIN
  rcall restorejk
  usbd UIN
  usbs SE0
reset1end:
; ST_SET_ADDR0
  casel ST_SET_ADDR0, setaddr0end
  rcall setjk
  usbd UINIT
; send SETUP
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs J
  nop2
  usbs10 K, K, J, K, J, K, J, K, J, K
  usbs10 J, K, J, K, J, K, K, J, K, J
  usbs3  SE0, SE0, J
; delay a little
  nop8
; send DATA0
  usbs7 K, J, K, J, K, J, K
  nop3
  usbs4 J, K, J, K
  nop2
  usbs10 J, K, J, K, J, K, J, K, K, J
  usbs10 J, K, J, K, J, K, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs4  K, J, K, J
  nop2
  usbs3 K, K, J
  nop2
  nop2
  usbs10 K, K, J, K, K, J, K, SE0, SE0, J
  usbd UIN
  usbs SE0
  rcall restorejk
setaddr0end:
; ST_SET_ADDR1
  case ST_SET_ADDR1, setaddr1end
  rcall setjk
  usbd UINIT
; send IN
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs4 J, K, K, J
  nop2
  usbs10 K, J, K, J, K, J, K, J, K, J 
  usbs10 K, J, K, K, J, K, J, SE0, SE0, J
; device will send DATA1 now.
  usbd UIN
  usbs SE0
; receive DATA1
  rcall recv_data1
  rcall restorejk
setaddr1end:
; ST_SET_CONF0
  casel ST_SET_CONF0, setconf0end
  rcall setjk
  usbd UINIT
; send SETUP
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs J
  nop2
  usbs10 K, K, J, K, K, J, K, J, K, J
  usbs7  K, J, K, J, K, K, J
  nop3
  usbs3 SE0, SE0, J
; delay a little
  nop8
; send DATA0
  usbs7 K, J, K, J, K, J, K
  nop3
  usbs4 J, K, J, K
  nop2
  usbs10 J, K, J, K, J, K, J, K, K, J
  usbs10 K, K, J, K, J, K, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs10 K, J, K, J, K, J, K, J, K, J
  usbs4  K, J, K, J
  nop3
  usbs10 K, J, J, K, J, J, K, K, J, K
  usbs6  K, J, K, SE0, SE0, J
  usbd UIN
  usbs SE0
  rcall restorejk
setconf0end:
; ST_SET_CONF1
  case ST_SET_CONF1, setconf1end
  rcall setjk
  usbd UINIT
; send IN
  usbs7 K, J, K, J, K, J, K
  nop2
  usbs4 J, K, K, J
  nop2
  usbs10 K, K, J, K, J, K, J, K, J, K
  usbs4  J, K, K, J
  nop3
  usbs3 SE0, SE0, J
; device will send DATA1 now.
  usbd UIN
  usbs SE0
; receive DATA1
  rcall recv_data1
  rcall restorejk
  ldi STATE, ST_NONE
setconf1end:
; ST_NONE
  case ST_NONE, noneend
  ldi ARG0, PORT1
  rcall check
  cpse ARG0, ZERO
  reti
  ldi ARG0, PORT2
  rcall check
  reti
noneend:
  inc STATE
  reti

; Entry point
start:
  ; Init variables
  clr UIN
  clr UOUT
  clr UINIT
  clr J
  clr K
  ldi STATE, ST_NONE
  ; Timer setup
  outi OCR0A, 46 ; ~1 ms
  outi TCCR0A, (1 << WGM01)
  outi TCCR0B, (1 << CS02)
  outi TIMSK0, (1 << OCIE0A)
  ; Init USB
  usbd UIN
  usbs SE0
  wait_ms 100
  sei
mainloop:
  rjmp mainloop

; See wait_ms
pwait_ms:
  outi TIFR0, (1 << OCF0A)
wait_ms_loop:
  in TMP, TIFR0
  andi TMP, (1 << OCF0A)
  breq wait_ms_loop
  dec ARG0
  brne pwait_ms
pnop8:
  nop
  ret

; Check device presence at port ARG0
check:
  in TMP, PINB
  and TMP, ARG0
  ldi TMP2, FS_J
  and TMP2, ARG0
  cp TMP, TMP2
  breq present
  mov TMP, UOUT
  and TMP, ARG0
  breq checkend
  disable ARG0
  rjmp checkend
present:
  mov TMP, UOUT
  and TMP, ARG0
  brne checkend
  ldi STATE, ST_INIT
  mov UINIT, ARG0
  ser ARG0
  ret
checkend:  
  clr ARG0
  ret

setjk:
  and J, UINIT
  and K, UINIT
  ret

restorejk:
  ldi J, FS_J
  and J, UOUT
  ldi K, FS_K
  and K, UOUT
  ret

recv_data1:
; delay 43 cycles
  ldi TMP, 14
recv_data1_loop:
  dec TMP
  brne recv_data1_loop
  nop
  usbd UINIT
; send ACK
  usbs10 K, J, K, J, K, J, K, K, J, J
  usbs4  K, J, J, K
  nop2
  usbs3 SE0, SE0, J
  usbd UIN
  usbs SE0
  ret
